//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<varying_data.cpp>
///	@path	~/src/object/
///	@date	2007/12/03
///	@desc	Varying data, object type is not consist.

#include "config/config.h"

#include "object/varying_data.h"

namespace xeres {

	// VaryingData
	VaryingData::VaryingData( void )
		// init type
		: m_type( VAR_VOID )
		, m_invariant( false )
	{
		m_data.u_asPtr = NULL;
	}

	// ~VaryingData
	VaryingData::~VaryingData( void )
	{
		DeleteData();
	}

	// get type name
	const WString& VaryingData::GetVarTypeName( void ) const
	{
#define	DEF_TYPE( TYPE_NAME ) { static const WString s_##TYPE_NAME##( TYPE_NAME ); return s_##TYPE_NAME##; }
		switch( m_type )
		{
		case VAR_VOID: DEF_TYPE( VOID_TYPE_CLASS_NAME );

		case VAR_BOOL: DEF_TYPE( BOOL_TYPE_CLASS_NAME );
		case VAR_INT: DEF_TYPE( INT_TYPE_CLASS_NAME );
		case VAR_FLOAT: DEF_TYPE( FLOAT_TYPE_CLASS_NAME );
		case VAR_PAIR: DEF_TYPE( PAIR_TYPE_CLASS_NAME );
		case VAR_VECTOR: DEF_TYPE( VECTOR_TYPE_CLASS_NAME );
		case VAR_QUAT: DEF_TYPE( QUAT_TYPE_CLASS_NAME );
		case VAR_COLOR: DEF_TYPE( COLOR_TYPE_CLASS_NAME );
		case VAR_MATRIX: DEF_TYPE( MATRIX_TYPE_CLASS_NAME );
		case VAR_STRING: DEF_TYPE( STRING_TYPE_CLASS_NAME );

		case VAR_IOBJECT :
			assert( m_data.u_asObject );
			return m_data.u_asObject->GetClassName();

		default:
			assert(false);
			__assume(false);
		}
#undef DEF_TYPE
	}

	// get type id
	const ClassID VaryingData::GetVarTypeID( void ) const
	{
		switch( m_type )
		{
		case VAR_VOID: return INVALID_CLASS_ID;

		case VAR_BOOL: return BOOL_TYPE_CLASS_ID;
		case VAR_INT: return INT_TYPE_CLASS_ID;
		case VAR_FLOAT: return FLOAT_TYPE_CLASS_ID;
		case VAR_PAIR: return PAIR_TYPE_CLASS_ID;
		case VAR_VECTOR: return VECTOR_TYPE_CLASS_ID;
		case VAR_QUAT: return QUAT_TYPE_CLASS_ID;
		case VAR_COLOR: return COLOR_TYPE_CLASS_ID;
		case VAR_MATRIX: return MATRIX_TYPE_CLASS_ID;
		case VAR_STRING: return STRING_TYPE_CLASS_ID;

		case VAR_IOBJECT :
			assert( m_data.u_asObject );
			return m_data.u_asObject->GetClassID();

		default:
			assert(false);
			__assume(false);
		}
	}

	// delete exist data by type
	inline void VaryingData::DeleteData( void )
	{
		//  FIXME: should use small object storage management
		switch( m_type )
		{
		case VAR_PAIR:
			assert( m_data.u_asPair );
			_aligned_free( m_data.u_asPair );
			m_data.u_asPair = NULL;
			break;			
		case VAR_VECTOR:
			assert( m_data.u_asVector );
			_aligned_free( m_data.u_asVector );
			m_data.u_asVector = NULL;
			break;
		case VAR_QUAT:
			assert( m_data.u_asQuat );
			_aligned_free( m_data.u_asQuat );
			m_data.u_asQuat = NULL;
			break;
		case VAR_COLOR:
			assert( m_data.u_asColor );
			_aligned_free( m_data.u_asColor );
			m_data.u_asColor = NULL;
			break;
		case VAR_MATRIX:
			assert( m_data.u_asMatrix );
			_aligned_free( m_data.u_asMatrix );
			m_data.u_asMatrix = NULL;
			break;
		case VAR_STRING:
			assert( m_data.u_asString );
			delete m_data.u_asString;
			m_data.u_asString = NULL;
			break;
		case VAR_IOBJECT:
			assert( m_data.u_asObject );
			m_data.u_asObject->ReleaseRef();
			m_data.u_asObject = NULL;
			break;
		}
		// continue to dispatch pointer-holding types!
	}

	// SetType
	void VaryingData::SetType( VaryingDataType type , bool invariant )
	{
		ChangeType( type );
		m_invariant = invariant;
	}

	// call when change type
	inline bool VaryingData::ChangeType( VaryingDataType newType )
	{
		if( m_type != newType )
		{
			if( m_invariant && m_type != VAR_VOID )
				return false;

			// delete original data
			DeleteData();

			// allocate new objects when old objects are deleted
			switch( newType )
			{
			case VAR_PAIR:
				m_data.u_asPair = static_cast< Pair<float>* >( _aligned_malloc( sizeof(Pair<float>) , 16 ) );
				break;			
			case VAR_VECTOR:
				m_data.u_asVector = static_cast< Vector<float>* >( _aligned_malloc( sizeof(Vector<float>) , 16 ) );
				break;
			case VAR_QUAT:
				m_data.u_asQuat = static_cast< Quat<float>* >( _aligned_malloc( sizeof(Quat<float>) , 16 ) );
				break;
			case VAR_COLOR:
				m_data.u_asColor = static_cast< Color4* >( _aligned_malloc( sizeof(Color4) , 16 ) );
				break;
			case VAR_MATRIX:
				m_data.u_asMatrix = static_cast< Matrix<float>* >( _aligned_malloc( sizeof(Matrix<float>) , 16 ) );
				break;
			case VAR_STRING:
				m_data.u_asString = new WString;
				break;
			case VAR_IOBJECT:
				m_data.u_asObject = NULL;
				break;
			}
			m_type = newType;
		}
		return true;
	}

	// set value
	bool VaryingData::SetBool( bool val )
	{
		if( m_invariant )
		{
			switch( m_type )
			{
			case VAR_BOOL:
				m_data.u_asBool = val;
				return true;
			case VAR_INT:
				m_data.u_asInt = ( val ? 1 : 0 );
				return true;
			case VAR_FLOAT:
				TRACE_WARNING( _S("VaryingData::SetBool: Using boolean on float value.") );
				m_data.u_asFloat = ( val ? 1.f : 0.f );
				return true;
			default:
				TRACE_ERROR( _S("VaryingData::SetBool: Value cannot be set to bool.") );
				return false;
			}
		}
		else
		{
			if( !ChangeType( VAR_BOOL ) )
				return false;
			m_data.u_asBool = val;
			return true;
		}
	}
	bool VaryingData::SetInt( int val )
	{
		if( m_invariant )
		{
			switch( m_type )
			{
			case VAR_BOOL:
				TRACE_ERROR( _S("VaryingData::SetInt: Truncating integer to boolean.") );
				m_data.u_asBool = ( val == 0 ? false : true );
				return true;
			case VAR_INT:
				m_data.u_asInt = val;
				return true;
			case VAR_FLOAT:
				m_data.u_asFloat = (float)val;
				return true;
			default:
				TRACE_ERROR( _S("VaryingData::SetInt: Value cannot be set to integer.") );
				return false;
			}
		}
		else
		{
			if( !ChangeType( VAR_INT ) )
				return false;
			m_data.u_asInt = val;
			return true;
		}
	}
	bool VaryingData::SetFloat( float val )
	{
		if( m_invariant )
		{
			switch( m_type )
			{
			case VAR_BOOL:
				TRACE_WARNING( _S("VaryingData::SetFloat: Truncating float to boolean.") );
				m_data.u_asBool = ( val == 0 ? false : true );
				return true;
			case VAR_INT:
				TRACE_WARNING( _S("VaryingData::SetFloat: Truncating float to integer.") );
				m_data.u_asInt = ( (int)val );
				return true;
			case VAR_FLOAT:
				m_data.u_asFloat = val;
				return true;
			default:
				TRACE_ERROR( _S("VaryingData::SetFloat: Value cannot be set to float.") );
				return false;
			}
		}
		else
		{
			if( !ChangeType( VAR_FLOAT ) )
				return false;
			m_data.u_asFloat = val;
			return true;
		}
	}
	bool VaryingData::SetPair( const Pair<float>& val )
	{
		if( m_invariant )
		{
			switch( m_type )
			{
			case VAR_PAIR:
				(*m_data.u_asPair) = val;
				return true;
			default:
				TRACE_ERROR( _S("CValueEntry::SetPair: Value cannot be set to pair.") );
				return false;
			}
		}
		else
		{
			if( !ChangeType( VAR_PAIR ) )
				return false;
			*(m_data.u_asPair) = val;
			return true;
		}
	}
	bool VaryingData::SetVector( const Vector<float>& val )
	{
		if( m_invariant )
		{
			switch( m_type )
			{
			case VAR_VECTOR:
				(*m_data.u_asVector) = val;
				return true;
			default:
				TRACE_ERROR( _S("VaryingData::SetVector: Value cannot be set to vector.") );
				return false;
			}
		}
		else
		{
			if( !ChangeType( VAR_VECTOR ) )
				return false;
			*(m_data.u_asVector) = val;
			return true;
		}
	}
	bool VaryingData::SetQuat( const Quat<float>& val )
	{
		if( m_invariant )
		{
			switch( m_type )
			{
			case VAR_QUAT:
				(*m_data.u_asQuat) = val;
				return true;
			default:
				TRACE_ERROR( _S("VaryingData::SetQuat: Value cannot be set to quaternion.") );
				return false;
			}
		}
		else
		{
			if( !ChangeType( VAR_QUAT ) )
				return false;
			*(m_data.u_asQuat) = val;
			return true;
		}
	}
	bool VaryingData::SetColor( const Color4& val )
	{
		if( m_invariant )
		{
			switch( m_type )
			{
			case VAR_COLOR:
				(*m_data.u_asColor) = val;
				return true;
			default:
				TRACE_ERROR( _S("VaryingData::SetColor: Value cannot be set to color.") );
				return false;
			}
		}
		else
		{
			if( !ChangeType( VAR_COLOR ) )
				return false;
			*(m_data.u_asColor) = val;
			return true;
		}
	}
	bool VaryingData::SetMatrix( const Matrix<float>& val )
	{
		if( m_invariant )
		{
			switch( m_type )
			{
			case VAR_MATRIX:
				(*m_data.u_asMatrix) = val;
				return true;
			default:
				TRACE_ERROR( _S("VaryingData::SetMatrix: Value cannot be set to matrix.") );
				return false;
			}
		}
		else
		{
			if( !ChangeType( VAR_MATRIX ) )
				return false;
			*(m_data.u_asMatrix) = val;
			return true;
		}
	}
	bool VaryingData::SetString( const WString& val )
	{
		if( m_invariant )
		{
			switch( m_type )
			{
			case VAR_BOOL:
				// boolean value hook
				if( _wcsicmp( val.c_str() , _S("true") ) == 0 )
				{
					m_data.u_asBool = true;
					return true;
				}
				else if( _wcsicmp( val.c_str() , _S("false") ) == 0 )
				{
					m_data.u_asBool = false;
					return true;
				}
				else
				{
					TRACE_ERROR( _S("VaryingData::SetString: Only true or false is valid to boolean data.") );
					return false;
				}
			case VAR_STRING:
				(*m_data.u_asString) = val;
				return true;
			default:
				TRACE_ERROR( _S("VaryingData::SetString: Value cannot be set to string.") );
				return false;
			}
		}
		else
		{
			if( !ChangeType( VAR_STRING ) )
				return false;
			(*m_data.u_asString) = val;
		}
		return true;
	}
	bool VaryingData::SetObject( RefWeak<IObject> val )
	{
		if( !ChangeType( VAR_IOBJECT ) )
			return false;

		if( val.IsNull() )
		{
			m_data.u_asPtr = NULL;
		}
		else
		{
			val.GetObject( m_data.u_asObject );
		}
		return true;
	}


	// get value
	bool VaryingData::GetBool( bool& val ) const
	{
		if( m_type != VAR_BOOL )
			return false;
		
		val = m_data.u_asBool;
		return true;
	}
	bool VaryingData::GetInt( int& val ) const
	{
		switch( m_type )
		{
		case VAR_INT:
			val = m_data.u_asInt;
			return true;
		case VAR_FLOAT:
			TRACE_WARNING( _S("VaryingData::GetInt: Truncating float to integer value.") );
			val = (int)m_data.u_asFloat;
			return true;
		default:
			return false;
		}
	}
	bool VaryingData::GetFloat( float& val ) const
	{
		switch( m_type )
		{
		case VAR_FLOAT:
			val = m_data.u_asFloat;
			return true;
		case VAR_INT:
			val = (float)m_data.u_asInt;
			return true;
		default:
			return false;
		}
	}
	bool VaryingData::GetPair( Pair<float>& val ) const
	{
		if( m_type != VAR_PAIR )
			return false;

		val = *(m_data.u_asPair);
		return true;
	}
	bool VaryingData::GetVector( Vector<float>& val ) const
	{
		if( m_type != VAR_VECTOR )
			return false;

		val = *(m_data.u_asVector);
		return true;
	}
	bool VaryingData::GetQuat( Quat<float>& val ) const
	{
		if( m_type != VAR_QUAT )
			return false;

		val = *(m_data.u_asQuat);
		return true;
	}
	bool VaryingData::GetColor( Color4& val ) const
	{
		if( m_type != VAR_COLOR )
			return false;

		val = *(m_data.u_asColor);
		return true;
	}
	bool VaryingData::GetMatrix( Matrix<float>& val ) const
	{
		if( m_type != VAR_MATRIX )
			return false;

		val = *(m_data.u_asMatrix);
		return true;
	}
	bool VaryingData::GetString( WString& val ) const
	{
		if( m_type != VAR_STRING )
			return false;

		assert( m_data.u_asString );
		val = *(m_data.u_asString);
		return true;
	}
	bool VaryingData::GetObject( Ref<IObject>& val ) const
	{
		if( m_type != VAR_IOBJECT )
			return false;

		val = m_data.u_asObject;
		return true;
	}

	// copy from
	bool VaryingData::DuplicateFrom( const VaryingData& val )
	{
		if( !ChangeType( val.m_type ) )
			return false;

		switch( m_type )
		{
		case VAR_VOID:
			m_data.u_asPtr = NULL;
			break;
		case VAR_BOOL:
			m_data.u_asBool = val.m_data.u_asBool;
			break;
		case VAR_INT:
			m_data.u_asInt = val.m_data.u_asInt;
			break;
		case VAR_FLOAT:
			m_data.u_asFloat = val.m_data.u_asFloat;
			break;
		case VAR_PAIR:
			*(m_data.u_asPair) = *(val.m_data.u_asPair);
			break;
		case VAR_VECTOR:
			*(m_data.u_asVector) = *(val.m_data.u_asVector);
			break;
		case VAR_QUAT:
			*(m_data.u_asQuat) = *(val.m_data.u_asQuat);
			break;
		case VAR_COLOR:
			*(m_data.u_asColor) = *(val.m_data.u_asColor);
			break;
		case VAR_MATRIX:
			*(m_data.u_asMatrix) = *(val.m_data.u_asMatrix);
			break;
		case VAR_STRING:
			*(m_data.u_asString) = *(val.m_data.u_asString);
			break;
		case VAR_IOBJECT:
			if( val.m_data.u_asObject )
				val.m_data.u_asObject->AddRef();
			m_data.u_asObject = val.m_data.u_asObject;
			break;
		}
		return true;
	}

} // namespace xeres
